#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <cerrno>
#include <unistd.h>
#include <cstring>
#include <memory>
using namespace std;


#define PROJ_ID 0x754
string PATHNAME(".");

#define CREATE_MSG (IPC_CREAT | IPC_EXCL)
#define GET_MSG IPC_CREAT
#define PERM 0666

#define default_msqid -1

#define CLIENT_TYPE 1
#define SERVER_TYPE 2 

#define MSIZE 1024

//不会直接使用父类
class MsgQueue{
private:
    struct msgbuf {
        long mtype;       /* message type, must be > 0 */
        char mtext[MSIZE];    /* message data */
    };

public:
    MsgQueue(int flag, const string& pathname = PATHNAME, int proj_id = PROJ_ID)
        :_msqid(default_msqid),
        _pathname(pathname),
        _projid(proj_id),
        _flag(flag)
    {
        GetKey();
        CreateOrGet_msq(); 
    }

    virtual ~MsgQueue(){
       Destory();
    }

    //向_msgid的消息队列中发送类型为type的数据
    void MsgSnd(long type, const string& msg){

        //处理发送数据
        struct msgbuf buf;
        memset(buf.mtext, 0, sizeof(buf.mtext));
        buf.mtype = type;
        //一开始把发送数据数量搞成MSIZE了，这是不对的，因为msg没那么多字符
        memcpy(buf.mtext, msg.c_str(), msg.size());

        //调用发送接口进行发送
        int snd_id = msgsnd(_msqid, &buf, sizeof(buf.mtext), 0);
        if(snd_id < 0){
            cerr << "send msg to queue error!" << endl; 
            exit(errno);
        }
    }

    //从_msgid的消息队列中接收类型为type的数据
    void MsgRcv(long type, string& msg){
        struct msgbuf buf;
        memset(buf.mtext, 0, sizeof(buf.mtext));
        ssize_t rcv_id = msgrcv(_msqid, &buf, sizeof(buf.mtext), type, 0);
        if(rcv_id < 0){
            cerr << "receive msg from queue error!" << endl; 
            cerr<< strerror(errno) << endl;
            exit(errno);
        }
        
        msg.append(buf.mtext);
    }

private:
    void CreateOrGet_msq(){
        _msqid = msgget(_key, _flag);
        if(_msqid < 0){
            cerr << "msgget error!" << endl;
            exit(errno);
        }
        else cout << "msgget success! msqid : " << _msqid << endl;
    }

    void Destory(){
        if(_flag == GET_MSG) return;
        int rm_msgid = msgctl(_msqid, IPC_RMID, nullptr);
        if(rm_msgid < 0){
            cerr << "remove msg queue error!" << endl;
            exit(errno);
        }
        else cout << "remove msg queue success!" << endl;
    }

    void GetKey(){
        _key = ftok(_pathname.c_str(), _projid);
        if(_key < 0){
            cerr << "ftok error!" << endl;
            exit(errno);
        }
        else cout << "ftok success! key : " << _key << endl;
    }
private:
    key_t _key;
    int _msqid;
    int _flag;
    string _pathname;
    int _projid; 
};



class Server : public MsgQueue{
public:
    Server(long type = SERVER_TYPE)
        :MsgQueue((CREATE_MSG | PERM), PATHNAME, PROJ_ID), 
        _type(type)
    {

    }
    ~Server(){}
private:
    long _type;
};

class Client : public MsgQueue{
public:
    Client(long type = CLIENT_TYPE)
        :MsgQueue(GET_MSG, PATHNAME, PROJ_ID), 
        _type(type)
    {

    }
    ~Client(){}
private:
    long _type;
};